/*
This is part of Screenbar, which is based in part on the Litestep
shell source code.

Copyright (C) 2008-2009 Jim Babcock
Copyright (C) 1997-2009 The LiteStep Development Team

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
#include "stdafx.h"

struct SaveState
{
	int numDesks;
	int numMonitors;
};

struct SavedStorageArea
{
	int x;
	int y;
};

struct SavedMonitor
{
	int focusedDesk;
};

bool restoreState()
{
	// Retrieve the first 4 bytes of saved state, which is the size of the rest of it
	int stateSize = 0;
	if(!SendMessage(GetLitestepWnd(), LM_RESTOREDATA, MAKEWPARAM(sizeof(int), MODULE_ID), (LPARAM)&stateSize)) {
		return false;
	}
	
	// Retrieve the saved state
	char *stateBuf = (char*)malloc(stateSize);
	if(!SendMessage(GetLitestepWnd(), LM_RESTOREDATA, MAKEWPARAM(stateSize, MODULE_ID+1), (LPARAM)stateBuf)) {
		return false;
	}
	string strState(stateBuf, stateSize);
	std::istringstream stream(strState);
	
	vwm->restoreState(stream);
	windowTracker->restore(stream);
	
	// Return true to indicate that there was a state to restore, and it was
	// restored successfully (not that we do much error checking)
	return true;
}

void saveState()
{
	std::ostringstream stream;
	
	vwm->saveState(stream);
	windowTracker->save(stream);
	
	// Send the saved state to Litestep so it'll survive a recycle
	string streamData = stream.str();
	
	int savedSize = streamData.length();
	SendMessage(GetLitestepWnd(), LM_SAVEDATA, MAKEWPARAM(sizeof(int), MODULE_ID), (LPARAM)&savedSize);
	
	void *savedData = malloc(savedSize);
	memcpy(savedData, (void*)streamData.c_str(), streamData.length());
	SendMessage(GetLitestepWnd(), LM_SAVEDATA, MAKEWPARAM(savedSize, MODULE_ID+1), (LPARAM)savedData);
	free(savedData);
}

void VWM::saveState(std::ostringstream &stream)
{
	SaveState state;
	state.numDesks = desktops.size();
	state.numMonitors = monitors->numMonitors();
	
	stream.write((char*)&state, sizeof(state));
	
	storageManager.save(stream);
	
	for(int ii=0; ii<state.numDesks; ii++)
		storageManager.saveDesk(desktops[ii], stream);
	
	for(int ii=0; ii<state.numMonitors; ii++)
	{
		Monitor *monitor = monitors->getMonitor(ii);
		SavedMonitor savedMonitor;
		savedMonitor.focusedDesk = monitor->getDesk()->getIndex();
		stream.write((char*)&savedMonitor, sizeof(savedMonitor));
	}
}

void VWM::restoreState(std::istream &stream)
{
	SaveState state;
	stream.read((char*)&state, sizeof(state));
	
	storageManager.restore(stream);
	
	for(int ii=0; ii<state.numDesks; ii++)
		desktops.push_back(storageManager.restoreDesk(stream));
	
	// Enforce minimum number of desktops
	while(numDesktops() < getMinDesktops())
		createDesktop(desktops.size());
	
	// Load task->desk mapping
	for(int ii=0; ii<state.numMonitors; ii++)
	{
		SavedMonitor savedMonitor;
		stream.read((char*)&savedMonitor, sizeof(savedMonitor));
		
		Monitor *monitor = monitors->getMonitor(ii);
		if(monitor) {
			VirtualDesktop *desk = desktops[savedMonitor.focusedDesk];
			desk->monitor = monitor;
			monitor->setDesk(desktops[savedMonitor.focusedDesk]);
		}
	}
	
	// TODO: Enforce maximum number of desktops, This can only happen when
	// modifying the theme.
}

void StorageManager::save(std::ostream &stream)
{
	int numAreas = areas.size();
	stream.write((char*)&numAreas, sizeof(numAreas));
	
	for(int ii=0; ii<numAreas; ii++)
	{
		SavedStorageArea savedArea;
		savedArea.x = areas[ii]->x;
		savedArea.y = areas[ii]->y;
		stream.write((char*)&savedArea, sizeof(savedArea));
	}
}

void StorageManager::restore(std::istream &stream)
{
	int numAreas;
	stream.read((char*)&numAreas, sizeof(numAreas));
	
	for(int ii=0; ii<numAreas; ii++)
	{
		SavedStorageArea savedArea;
		stream.read((char*)&savedArea, sizeof(savedArea));
		
		areas.push_back(new StorageArea(this, ii, savedArea.x, savedArea.y));
	}
}

struct SavedDesk
{
	int index;
	int storageSlot;
};

void StorageManager::saveDesk(VirtualDesktop *desk, std::ostream &stream)
{
	SavedDesk saved;
	
	StorageArea *storage = dynamic_cast<StorageArea*>(desk->getOffScreenStorage());
	saved.index = desk->getIndex();
	saved.storageSlot = storage->index;
	
	stream.write((char*)&saved, sizeof(saved));
}

VirtualDesktop *StorageManager::restoreDesk(std::istream &stream)
{
	SavedDesk saved;
	stream.read((char*)&saved, sizeof(saved));
	
	return new VirtualDesktop(areas[saved.storageSlot]);
}


struct SavedTask
{
	HWND representative;
	int desk;
};

void WindowTracker::save(std::ostream &stream)
{
	vector<TaskData*> savedTasks;
	getTasks(NULL, savedTasks);
	
	int numTasks = savedTasks.size();
	stream.write((char*)&numTasks, sizeof(numTasks));
	
	for(unsigned ii=0; ii<savedTasks.size(); ii++)
	{
		TaskData *task = savedTasks[ii];
		SavedTask savedTask;
		savedTask.representative = task->getRepresentativeWindow()->handle;
		savedTask.desk = task->getRepresentativeWindow()->desk->getIndex();
		stream.write((char*)&savedTask, sizeof(savedTask));
	}
}

void WindowTracker::restore(std::istream &stream)
{
	int numTasks;
	stream.read((char*)&numTasks, sizeof(numTasks));
	
	for(int ii=0; ii<numTasks; ii++)
	{
		SavedTask savedTask;
		stream.read((char*)&savedTask, sizeof(savedTask));
		
		HWND handle = savedTask.representative;
		if(!IsWindow(handle))
			continue;
		
		windowCreated(handle);
		WindowData *window = getWindow(handle);
		if(window->isMinimized())
			window->desk = vwm->getDesk(savedTask.desk);
	}
}
